﻿<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>开始录音</title>

    <link rel="stylesheet" href="../../../layui.css?v=1">
    <style>
        .recoder{
		    /* position: fixed;
		    left: calc(50% - 100px);
		    top: calc(50% - 100px); */
		    position: relative;
		    box-shadow: 0 0 32px rgba(0, 0, 0, 0.15);
		    width: 200px;
		    height: 200px;
		    background: #fff;
	    }
	    .sound-waves{
		    display: flex;
		    justify-content: center;
		    align-items: center;
		    height: 70px;
	    }
	    .wavesItem{
		    border: 1px solid #f2f2f2;
		    width: 4px;
		    background: green;
		    margin: 2px;
	    }
	    .tipText{
		    text-align: center;
		    font-size: 14px;
		    position: absolute;
		    bottom: 70px;
		    width: 100%;
	    }
	    .holdBtn{
		    width: 50px;
		    height: 50px;
		    border-radius: 25px;
		    text-align: center;
		    padding: 0;
		    position: absolute;
		    bottom: 10px;
		    left: calc(50% - 25px);
		    background: green;
	    }
	    .microphone{
		    height: 50px;
		    width: 30px;
		    margin: 0 !important;
	    }
    </style>
</head>
<body>

	<div id="recoderDiv">
		<div class="recoder">
			<div class="sound-waves">
				<div v-for="(iheight, index) in randomheight"
					 :key="index"
					 class="wavesItem"
					 :style="{'height': iheight + 'px'}"></div>
			</div>
			<div class="tipText">按住开始录音</div>
			<button class="holdBtn"
				@touchstart="mouseStart"
				@touchend="mouseEnd('audio')"
				@mousedown.prevent="mouseStart"
				@mouseup.prevent="mouseEnd('audio')">
				<svg t="1571062524943" class="icon microphone" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2945" width="200" height="200"><path d="M714 605.9v-338c0-111.4-90.6-202-202-202s-202 90.6-202 202v338c0 104.6 80 190.9 182 201v111.2H379c-11 0-20 9-20 20s9 20 20 20h266c11 0 20-9 20-20s-9-20-20-20H532V806.9c102-10.1 182-96.4 182-201z m-202-500c89.3 0 162 72.7 162 162v318H350v-318c0-89.4 72.7-162 162-162z m-160.8 520h321.5c-9.9 79.9-78.2 142-160.8 142s-150.8-62.1-160.7-142z" fill="#565656" p-id="2946"></path><path d="M455.4 193.8m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2947"></path><path d="M455.4 303.5m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2948"></path><path d="M455.4 413.2m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2949"></path><path d="M455.4 522.9m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2950"></path><path d="M398.9 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2951"></path><path d="M398.9 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2952"></path><path d="M398.9 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2953"></path><path d="M512 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2954"></path><path d="M512 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2955"></path><path d="M512 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2956"></path><path d="M568.6 193.8m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2957"></path><path d="M568.6 303.5m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2958"></path><path d="M568.6 413.2m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2959"></path><path d="M568.6 522.9m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2960"></path><path d="M625.1 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2961"></path><path d="M625.1 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2962"></path><path d="M625.1 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2963"></path></svg>
			</button>
		</div>
	</div>

	<!--<div class="recoderDiv">
		<div class="recoder" v-show="recoderVisible">
			<div class="sound-waves">
				<div v-for="(iheight, index) in randomheight"
					 :key="index"
					 class="wavesItem"
					 :style="{'height': iheight + 'px'}"></div>
			</div>
			<div class="tipText">按住开始录音</div>
			<a-button type="primary"
					  class="holdBtn"
					  @touchstart="mouseStart"
					  @touchend="mouseEnd('audio')"
					  @mousedown.prevent="mouseStart"
					  @mouseup.prevent="mouseEnd('audio')">
				<svg t="1571062524943" class="icon microphone" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2945" width="200" height="200"><path d="M714 605.9v-338c0-111.4-90.6-202-202-202s-202 90.6-202 202v338c0 104.6 80 190.9 182 201v111.2H379c-11 0-20 9-20 20s9 20 20 20h266c11 0 20-9 20-20s-9-20-20-20H532V806.9c102-10.1 182-96.4 182-201z m-202-500c89.3 0 162 72.7 162 162v318H350v-318c0-89.4 72.7-162 162-162z m-160.8 520h321.5c-9.9 79.9-78.2 142-160.8 142s-150.8-62.1-160.7-142z" fill="#565656" p-id="2946"></path><path d="M455.4 193.8m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2947"></path><path d="M455.4 303.5m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2948"></path><path d="M455.4 413.2m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2949"></path><path d="M455.4 522.9m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2950"></path><path d="M398.9 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2951"></path><path d="M398.9 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2952"></path><path d="M398.9 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2953"></path><path d="M512 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2954"></path><path d="M512 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2955"></path><path d="M512 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2956"></path><path d="M568.6 193.8m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2957"></path><path d="M568.6 303.5m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2958"></path><path d="M568.6 413.2m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2959"></path><path d="M568.6 522.9m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2960"></path><path d="M625.1 248.7m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2961"></path><path d="M625.1 358.4m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2962"></path><path d="M625.1 468.1m-18.4 0a18.4 18.4 0 1 0 36.8 0 18.4 18.4 0 1 0-36.8 0Z" fill="#565656" p-id="2963"></path></svg>
			</a-button>
		</div>
	</div>-->


	<!--<textarea title="消息模版" id="LAY_tpl" style="display:none;">
	{{# layui.each(d.data, function(index, item){
	  if(item.id == parent.layui.layim.cache().mine.id){ }}
		<li class="layim-chat-mine"><div class="layim-chat-user"><img src="{{ item.avatar }}"><cite><i>{{ layui.data.date(item.timestamp) }}</i>{{ item.username }}</cite></div><div class="layim-chat-text">{{ layui.layim.content(item.content) }}</div></li>
	  {{# } else { }}
		<li><div class="layim-chat-user"><img src="{{ item.avatar }}"><cite>{{ item.username }}<i>{{ layui.data.date(item.timestamp) }}</i></cite></div><div class="layim-chat-text">{{ layui.layim.content(item.content) }}</div></li>
	  {{# }
	}); }}
	</textarea>-->

	<script src="../../../../layui.js?v=1"></script>
	<script src="../../../../../js/vue2.6.10.js"></script>
	<script src="recordAudio.js" type="model"></script>
	<script>

		layui.config({
			base: "/js/"
		}).use(['layim', 'layer'], function () {
            var layim = layui.layim
                , layer = layui.layer
                , $ = layui.jquery;

            var param = parent.layer.data;
            console.log(parent);
			
			window.URL = window.URL || window.webkitURL;
			navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

			const HZRecorder = function(stream, config){
				config = config || {};
				config.sampleBits = config.sampleBits || 16; // 采样数位 8, 16
				config.sampleRate = config.sampleRate || (16000); // 采样率(1/6 44100)

				var context = new AudioContext();
				var audioInput = context.createMediaStreamSource(stream);
				var recorder = context.createScriptProcessor(16 * 1024, 1, 1);
				var emptyCheckCount = 0;
				var emptyDatacount = 0;

				const interpolateArray = (data, newSampleRate, oldSampleRate) => {
					var fitCount = Math.round(data.length * (newSampleRate / oldSampleRate));
					var newData = new Array();
					var springFactor = new Number((data.length - 1) / (fitCount - 1));
					newData[0] = data[0]; // for new allocation
					for(var i = 1; i < fitCount - 1; i++){
						var tmp = i * springFactor;
						var before = new Number(Math.floor(tmp)).toFixed();
						var after = new Number(Math.ceil(tmp)).toFixed();
						var atPoint = tmp - before;
						newData[i] = linearInterpolate(data[before], data[after], atPoint);
					}
					newData[fitCount - 1] = data[data.length - 1]; // for new allocation
					return newData;
				};
				const linearInterpolate = (before, after, atPoint) => {
					return before + (after - before) * atPoint;
				};

				var audioData = {
					size: 0, // 录音文件长度
					buffer: [], // 录音缓存
					inputSampleRate: context.sampleRate, // 输入采样率
					inputSampleBits: 16, // 输入采样数位 8, 16
					outputSampleRate: config.sampleRate, // 输出采样率
					outputSampleBits: config.sampleBits, // 输出采样数位 8, 16
					input: function(data){
						this.buffer.push(new Float32Array(data));
						this.size += data.length;
					},
					compress: function(){
						// change sampleRate
						var data = new Float32Array(this.size);
						var offset = 0;
						for(var i = 0; i < this.buffer.length; i++){
							data.set(this.buffer[i], offset);
							offset += this.buffer[i].length;
						}

						var result = interpolateArray(data, this.outputSampleRate, this.inputSampleRate);

						return result;
					},
					encodeWAV: function(){
						var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
						var sampleBits = Math.min(this.inputSampleBits, this.outputSampleBits);
						var bytes = this.compress();
						var dataLength = bytes.length * (sampleBits / 8);
						var buffer = new ArrayBuffer(44 + dataLength);
						var data = new DataView(buffer);

						var channelCount = 1;// 单声道
						var offset = 0;

						var writeString = function(str){
							for(var i = 0; i < str.length; i++){
								data.setUint8(offset + i, str.charCodeAt(i));
							}
						};

						// 资源交换文件标识符
						writeString("RIFF");
						offset += 4;
						// 下个地址开始到文件尾总字节数,即文件大小-8
						data.setUint32(offset, 36 + dataLength, true);
						offset += 4;
						// WAV文件标志
						writeString("WAVE");
						offset += 4;
						// 波形格式标志
						writeString("fmt ");
						offset += 4;
						// 过滤字节,一般为 0x10 = 16
						data.setUint32(offset, 16, true);
						offset += 4;
						// 格式类别 (PCM形式采样数据)
						data.setUint16(offset, 1, true);
						offset += 2;
						// 通道数
						data.setUint16(offset, channelCount, true);
						offset += 2;
						// 采样率,每秒样本数,表示每个通道的播放速度
						data.setUint32(offset, sampleRate, true);
						offset += 4;
						// 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
						data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true);
						offset += 4;
						// 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
						data.setUint16(offset, channelCount * (sampleBits / 8), true);
						offset += 2;
						// 每样本数据位数
						data.setUint16(offset, sampleBits, true);
						offset += 2;
						// 数据标识符
						writeString("data");
						offset += 4;
						// 采样数据总数,即数据总大小-44
						data.setUint32(offset, dataLength, true);
						offset += 4;
						// 写入采样数据
						if(sampleBits === 8){
							for(var i = 0; i < bytes.length; i++, offset++){
								var s = Math.max(-1, Math.min(1, bytes[i]));
								var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
								val = parseInt(255 / (65535 / (val + 32768)));
								data.setInt8(offset, val, true);
							}
						}
						else{
							for(var i = 0; i < bytes.length; i++, offset += 2){
								var s = Math.max(-1, Math.min(1, bytes[i]));
								data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
							}
						}

						return new Blob([data], { type: "audio/wav" });
					}
				};

				// 开始录音
				this.start = function(){
					audioInput.connect(recorder);
					recorder.connect(context.destination);
				};
				this.isEmptyData = function(d){
					// 基本确定采样得到的语音数据是空，即没有语音输入
					// 非常简单的基于音量的端点检测算法
					// 这个循环加操作执行用时不超过1ms
					var l = Math.floor(d.length / 10);
					var vol = 0;
					for(var i = 0; i < l; i++){
						vol += Math.abs(d[i * 10]);
					}
					emptyCheckCount++;
					if(vol < 10){
						emptyDatacount++;

						if(emptyDatacount > 10){
							// HZRecorder = false;
							// this.stop();
							console.log("stoped");
							return true;
						}
					}
					else{
						emptyDatacount = 0;
					}
					return false;
				};
				// 停止
				this.stop = function(){
					if(context.state === "running"){
						context.close();
					}

					recorder.disconnect();
				};

				// 获取音频文件
				this.getBlob = function(){
					this.stop();
					return audioData.encodeWAV();
				};

				// 回放
				this.play = function(audio){
					audio.src = window.URL.createObjectURL(this.getBlob());
				};

				// 音频采集
				recorder.onaudioprocess = (e) => {
					audioData.input(e.inputBuffer.getChannelData(0));

					// console.log(this.isEmptyData(e.inputBuffer.getChannelData(0)))
					// record(e.inputBuffer.getChannelData(0));
				};
			};
			HZRecorder.setErrorInfoText = (errorMessage) => {
				HZRecorder.errorMessage = errorMessage;
			};

			// 是否支持录音
			HZRecorder.canHZRecorder = (navigator.getUserMedia != null);
			// 获取录音机
			HZRecorder.get = function(callback, config){
				if(callback){
					if(navigator.getUserMedia){
						navigator.getUserMedia(
							{ audio: true } // 只启用音频
							, function(stream){
								let rec = new HZRecorder(stream, config);
								callback(rec);
							}
							, function(error){
								switch(error.code || error.name){
								case "PERMISSION_DENIED":
								case "PermissionDeniedError":
									layer.msg("用户拒绝提供信息。");
									break;
								case "NOT_SUPPORTED_ERROR":
								case "NotSupportedError":
									layer.msg("浏览器不支持硬件设备。");
									break;
								case "MANDATORY_UNSATISFIED_ERROR":
								case "MandatoryUnsatisfiedError":
									layer.msg("无法发现指定的硬件设备。");
									break;
								default:
									// layer.msg("无法打开麦克风。异常信息:" + (error.code || error.name));
									layer.msg("当前浏览器不支持录音功能。(建议使用Chrome)");
									break;
								}
							});
					}
					else{
						layer.msg("当前浏览器不支持录音功能。");
					}
				}
			};

			//  --------------------------------------------------------------------------------------------------------------------------

			let vm = new Vue({
				el: '#recoderDiv',
				data() {
					return {
						form: {
							time: "按住说话(60秒)",
							audioUrl: ""
						},
						num: 60, // 按住说话时间
						recorder: null,
						interval: "",
						audioFileList: [], // 上传语音列表
						startTime: "", // 语音开始时间
						endTime: "", // 语音结束
						audioSrc: "",

						randomheight: [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
						runAnimation: false,
						recoderVisible: true
					}
				},
				watch: {
				},
				methods: {
					// 清除定时器
					clearTimer(){
						if(this.interval){
							this.num = 60;
							clearInterval(this.interval);
						}
					},
					onRandom(){
						let me = this;
						let _randomheight = this.randomheight.concat([]);
						let i = 0;
						this.$data.randomheight = _randomheight;
						if(this.runAnimation){
							for(i; i < this.randomheight.length; i++){
								_randomheight[i] = (60 * Math.random().toFixed(2)) + 10;
							}
							setTimeout(function(){me.onRandom(); }, 500);
						}
					},
					//hide(){
					//	this.recoderVisible = false;
					//},
					//show(){
					//	this.recoderVisible = true;
					//},
					// 长按说话
					mouseStart(e){
						e.preventDefault();
						this.$data.runAnimation = true;
						this.onRandom();
						this.clearTimer();
                        this.startTime = new Date().getTime();

						HZRecorder.get(rec => {
							// 当首次按下时，要获取浏览器的麦克风权限，所以这时要做一个判断处理
							if(rec){
								// 首次按下，只调用一次
								if(this.flag){
									this.mouseEnd();
									this.flag = false;
								}
								else{
									this.recorder = rec;
									this.interval = setInterval(() => {
										if(this.num <= 0){
											this.recorder.stop();
											this.num = 60;
											this.clearTimer();
										}
										else{
											this.num--;
											this.time = "松开结束（" + this.num + "秒）";
											this.recorder.start();
										}
									}, 1000);
								}
							}
						});
					},

					// 松开时上传语音
                    mouseEnd(type) {
                        let that = this;
						this.$data.runAnimation = false;
						this.$data.randomheight = this.randomheight.map(i => i = 30);
						//this.hide();
						this.clearTimer();
                        this.endTime = new Date().getTime();

						if(this.recorder){
							this.recorder.stop();
							// 重置说话时间
							this.num = 60;
							this.time = "按住说话（" + this.num + "秒）";
							// 获取语音二进制文件
							let blob = this.recorder.getBlob();
							// 发送语音功能
                            if (type === "audio") {

                                console.log(parent.layer.data)
								
								this.$data.audioSrc = param.webIM.default.utils.parseDownloadResponse.call(param.conn, blob);
								//const { name, params } = Vue.$route;

								let uri = {
									url: param.webIM.default.utils.parseDownloadResponse.call(param.conn, blob),
									filename: "audio",
									filetype: "audio",
									data: blob
                                };

								layui.each(param.media, function (index, item) {
									item && item({
									type: 'chatroom',
									useId: param.thatChat.data.id,
									file: uri
								});
								});

								//that.sendRecorder({
								//	type: 'chatroom',
								//	useId: '1b1a11a670ea06ad37',
								//	file: uri
								//});

								//disp.fire("im.test", "测试");
							}

						}
                    },
				},
				mounted() {
					//var _this = this;
                
				}
			})



            //var html = laytpl(LAY_tpl.value).render({
            //    data: res.data
            //});
            //$('#LAY_view').html(html);

        });

	</script>
</body>
</html>
